package graphs;

import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import model.Junction;
import model.Road;
import model.Route;


public abstract class Graph {
	protected List<Junction> junctions = new LinkedList<Junction>();
	
	public abstract Junction getStart();
	public abstract Junction getEnd();
	public void check() {
		assert (getStart() != null) : "Graph getStart() cannot return null";
		assert (getEnd() != null) : "Graph getEnd() cannot return null";
		
		checkRoadValidity();
		checkRouteAvailability();
	}
	
	private void checkRouteAvailability() {
		if(getAllRoutes().size() == 0) {
			System.out.println("Graph Check failed. No routes found");
			System.exit(-1);
		}
	}
	
	private void checkRoadValidity() {
		for (Road r : getRoads()) {
			if(!r.isValid())
			{
				System.out.println("GraphCheck failed: Road \"" + r + "\" is not valid.");
				System.exit(1);
			}
		}
	}
	
	public Set<Road> getRoads(){
		Set<Road> result = new HashSet<Road>();
		for (Junction j : junctions) {
			result.addAll(j.getRoads());
		}
		return result;
	}
	
	public Set<Route> getAllRoutes(){
		Route r = new Route(getStart());
		return getAllRoutes(r);
	}
	
	private Set<Route> getAllRoutes(Route part){
		Junction last = part.getLastJunction();
		if(last == null)
			last = getStart();
		//Order of iteration is guaranteed to be the same.
		Set<Route> routes = new LinkedHashSet<Route>();
		if(last == getEnd()){
			routes.add(part);
			return routes;
		}
		
		for (Road r : last.getRoads()) {
			if(!part.contains(r)) {
				Route nextPart = part.copy();
				nextPart.add(r);
				routes.addAll(getAllRoutes(nextPart));
			}
		}
		return routes;
	}
	public void reset(double amount) {
		for (Road r : getRoads()) {
			r.reset(amount);
		}
	}
}
